// Built-In API Source Code
//
// IMPORTANT: this file should be in sync with `builtin.go`
//
//
namespace ::


type Bool(~String) native
const No  Bool native('BoolNo')
const Yes Bool native('BoolYes')
function Not { p Bool }         Bool native('BoolNot')
operator and { p Bool, q Bool } Bool native('BoolAnd')
operator or  { p Bool, q Bool } Bool native('BoolOr')
operator ==  { p Bool, q Bool } Bool native('BoolEqual')
operator assert[T] { ok Bool, k Lambda[None,T] } T native('BoolAssert')

type Ordering(~String) enum { L<R, L=R, L>R }
operator == { o1 Ordering, o2 Ordering } Bool { (([Int]) o1 == o2) }
type == [T] interface { Operator Lambda[Pair[T,T],Bool] }
type <  [T] interface { Operator Lambda[Pair[T,T],Bool] }
type <> [T] interface { Operator Lambda[Pair[T,T],Ordering] }
operator != [T] { a T, b T } { == ==[T] } Bool { Not(==(a,b)) }
operator >  [T] { a T, b T } { < <[T] }   Bool { <(b,a) }
operator <= [T] { a T, b T } { < <[T] }   Bool { Not(<(b,a)) }
operator >= [T] { a T, b T } { < <[T] }   Bool { Not(<(a,b)) }
function Min[T] { a T, b T } { < <[T] }   T    { if (<(a,b)) { a } else { b } }
function Max[T] { a T, b T } { < <[T] }   T    { if (<(b,a)) { a } else { b } }

type Int(~String) native
operator + { a Int, b Int } Int native('IntPlus')
operator - { a Int, b Int } Int native('IntMinus')
operator * { a Int, b Int } Int native('IntTimes')
operator / { a Int, b Int } Int native('IntQuo')
operator % { a Int, b Int } Int native('IntRem')
operator ^ { a Int, b Int } Int native('IntPow')
operator == { a Int, b Int } Bool     native('IntEqual')
operator <  { a Int, b Int } Bool     native('IntLessThan')
operator <> { a Int, b Int } Ordering native('IntCompare')

type Float(~String) native
operator + { x Float, y Float } Float native('FloatPlus')
operator - { x Float, y Float } Float native('FloatMinus')
operator * { x Float, y Float } Float native('FloatTimes')
operator / { x Float, y Float } Float native('FloatQuo')
operator % { x Float, y Float } Float native('FloatRem')
operator ^ { x Float, y Float } Float native('FloatPow')
operator == { x Float, y Float } Bool native('FloatEqual')
operator <  { x Float, y Float } Bool native('FloatLessThan')
function Floor { x Float } Float native('Floor')
function Ceil  { x Float } Float native('Ceil')
function Round { x Float } Float native('Round')
function Sqrt  { x Float } Float native('Sqrt')
function Cbrt  { x Float } Float native('Cbrt')
function Exp   { x Float } Float native('Exp')
function Log   { x Float } Float native('Log')
function Sin   { x Float } Float native('Sin')
function Cos   { x Float } Float native('Cos')
function Tan   { x Float } Float native('Tan')
function Asin  { x Float } Float native('Asin')
function Acos  { x Float } Float native('Acos')
function Atan  { x Float } Float native('Atan')
function Atan2 { y Float, x Float } Float native('Atan2')
const E  Float { Exp(1) }
const PI Float { Acos(-1) }
method Float.Int Int   native('FloatInt')
method Int.Float Float native('IntFloat')
method Float.Normal   Bool native('FloatNormal')
method Float.NaN      Bool native('FloatNaN')
method Float.Infinite Bool native('FloatInfinite')
const NaN Float { (0.0 / 0) }
const +Infinity Float { (1.0 / 0) }
const -Infinity Float { (-1.0 / 0) }

type Char(~String) native
function Char { value Int } Char native('Char')
method Char.Int Int native('CharInt')
method Char.Utf8Size  Int   native('CharUtf8Size')
method Char.Utf8Bytes Bytes native('CharUtf8Bytes')
operator == { a Char, b Char } Bool     native('CharEqual')
operator <  { a Char, b Char } Bool     native('CharLessThan')
operator <> { a Char, b Char } Ordering native('CharCompare')

type String native
function variadic String { fragments List[~String] } String native('String')
function StringFromChars { chars List[Char] } String native('StringFromChars')
method String.Chars List[Char] native('StringChars')
method String.Utf8Size  Int   native('StringUtf8Size')
method String.Utf8Bytes Bytes native('StringUtf8Bytes')
operator == { a String, b String } Bool     native('StringEqual')
operator <  { a String, b String } Bool     native('StringLessThan')
operator <> { a String, b String } Ordering native('StringCompare')
operator shift { s String } Maybe[Pair[Char,String]] native('StringShift')
operator join  { l List[String], sep String } String native('ListStringJoin')
operator split { s String, sep String } List[String] native('StringSplit')
operator cut   { s String, sep String } Maybe[Pair[String,String]] native('StringCut')
operator advance { s String, regexp String } Maybe[Pair[String,String]] native('StringAdvance')
operator satisfy { s String, regexp String } Bool native('StringSatisfy')
operator replace { s String, regexp String, f Lambda[String,String] } String native('StringReplace')

type ~String interface { String String }
method Bool.String     String native('BoolString')
method Ordering.String String native('OrderingString')
method Int.String   String native('IntString')
method Float.String String native('FloatString')
method Char.String  String native('CharString')

type Bytes native
function variadic Bytes { fragments List[Bytes] } Bytes native('Bytes')
method Bytes.Size Int native('BytesSize')
method Bytes.Utf8String String native('BytesUtf8String')

type Asset native

type List[T] native
function variadic List[T] { items List[T] } List[T] { items }
function variadic ListConcat[T] { lists List[List[T]] } List[T] native('ListConcat')
function Cons[T] { head T, tail List[T] } List[T] native('Cons')
function Count { n Int } List[Int] native('Count')
method List.Empty Bool native('ListEmpty')
method List.First Maybe[T] native('ListFirst')
method List.$ $[T] { ${items:this} }
operator shift[T]      { l List[T] } Maybe[Pair[T,List[T]]] native('ListShift')
operator prepend[T]    { l List[T], item T }    List[T] { Cons(item, l) }
operator sort[T]       { l List[T] } { < <[T] } List[T] native('ListSort')
operator with-index[T] { l List[T] } List[Pair[T,Int]]  native('ListWithIndex')
operator map[A,B]    { l List[A], f Lambda[A,B] } List[B] native('ListMap')
operator map?[A,B]   { l List[A], f Lambda[A,Maybe[B]] } List[B] native('ListDeflateMap')
operator map*[A,B]   { l List[A], f Lambda[A,List[B]] } List[B] native('ListFlatMap')
operator map2[A,B,C] { l List[A], m List[B], f Lambda[Pair[A,B],C] } List[C] native('ListZipMap')
operator filter[T]   { l List[T], f Lambda[T,Bool] } List[T] native('ListFilter')
operator scan[A,B]   { l List[A], v B, f Lambda[Pair[B,A],B] } List[B] native('ListScan')
operator fold[A,B]   { l List[A], v B, f Lambda[Pair[B,A],B] } B native('ListFold')

type Queue[T] native
function variadic Queue[T] { items List[T] } Queue[T] native('Queue')
method Queue.Empty Bool native('QueueEmpty')
method Queue.Size  Int  native('QueueSize')
method Queue.First Maybe[T] native('QueueFirst')
method Queue.List List[T] native('QueueList')
operator shift[T]  { q Queue[T] } Maybe[Pair[T,Queue[T]]] native('QueueShift')
operator append[T] { q Queue[T], item T } Queue[T] native('QueueAppend')

type Heap[T] native
function variadic Heap[T] { items List[T] } { < <[T] } Heap[T] native('Heap')
method Heap.Empty Bool native('HeapEmpty')
method Heap.Size  Int  native('HeapSize')
method Heap.First Maybe[T] native('HeapFirst')
method Heap.List List[T] native('HeapList')
operator shift[T]  { h Heap[T] } Maybe[Pair[T,Heap[T]]] native('HeapShift')
operator insert[T] { h Heap[T], item T } Heap[T] native('HeapInsert')

type Set[T] native
function variadic Set[T] { items List[T] } { <> <>[T] } Set[T] native('Set')
method Set.Empty Bool native('SetEmpty')
method Set.Size  Int  native('SetSize')
method Set.List List[T] native('SetList')
operator has[T] { s Set[T], item T } Bool native('SetHas')
operator delete[T] { s Set[T], item T } Set[T] native('SetDelete')
operator insert[T] { s Set[T], item T } Set[T] native('SetInsert')

type Map[K,V] native
function variadic Map[K,V] { entries List[Pair[K,V]] } { <> <>[K] } Map[K,V] native('Map')
method Map.Empty Bool native('MapEmpty')
method Map.Size  Int  native('MapSize')
method Map.Keys    List[K] native('MapKeys')
method Map.Values  List[V] native('MapValues')
method Map.Entries List[Pair[K,V]] native('MapEntries')
operator has[K,V] { m Map[K,V], key K } Bool native('MapHas')
operator lookup[K,V] { m Map[K,V], key K } Maybe[V] native('MapLookup')
operator delete[K,V] { m Map[K,V], key K } Map[K,V] native('MapDelete')
operator insert[K,V] { m Map[K,V], entry Pair[K,V] } Map[K,V] native('MapInsert')

type Has[X,T] interface { Operator Lambda[Pair[X,T],Bool] }
operator in[X,T] { item T, x X } { has Has[X,T] } Bool { has(x,item) }
operator not-in[X,T] { item T, x X } { has Has[X,T] } Bool { Not((item in x)) }

/// observable
type $[T] native
function variadic $[T] { items List[T] } $[T] native('Observable')
function WithCancelSignal[T] { cancel $[None], o $[T] } $[T] native('WithCancelSignal')
function WithCancelTimeout[T] { ms Int, o $[T] } $[T] native('WithCancelTimeout')
function CompleteOnEmit[T] { o $[T] } $[None] native('CompleteOnEmit')
function SetTimeout { ms Int } $[None] native('SetTimeout')
function SetInterval { ms Int, n Int default(-1) } $[Int] native('SetInterval')
function Go[T] { k Lambda[None,T] } $[T] native('Go')
function Throw[T] { err Error } $[T] native('Throw')
function Crash[T] { err Error } $[T] native('Crash')
const Noop   $[None]   native('Noop')
const Random $[Float]  native('Random')
const UUID   $[String] native('UUID')
const SeqNum $[Int]    native('SeqNum')
operator share[T] { o $[T] } $[$[T]] native('ObservableShare')
operator catch[T] { o $[T], f Lambda[Pair[Error,$[T]],$[T]] } $[T] native('ObservableCatch')
operator retry[T] { o $[T], n Int default(-1) } $[T] native('ObservableRetry')
operator distinct-until-changed[T] { o $[T] } { == ==[T] } $[T] native('ObservableDistinctUntilChanged')
operator with-latest-from[T,X] { o $[T], x $[X] } $[Pair[T,X]] native('ObservableWithLatestFrom')
operator with-cycle[T,X] { o $[T], l List[X] } $[Pair[T,X]] native('ObservableWithCycle')
operator with-index[T] { o $[T] } $[Pair[T,Int]] native('ObservableWithIndex')
operator with-time[T] { o $[T] } $[Pair[T,Time]] native('ObservableWithTime')
operator delay-run[T] { o $[T], ms Int } $[T] native('ObservableDelayRun')
operator delay-values[T] { o $[T], ms Int } $[T] native('ObservableDelayValues')
operator start-with[T] { o $[T], item T } $[T] native('ObservableStartWith')
operator end-with[T]   { o $[T], item T } $[T] native('ObservableEndWith')
operator throttle[T] { o $[T], f Lambda[T,$[None]] } $[T] native('ObservableThrottle')
operator debounce[T] { o $[T], f Lambda[T,$[None]] } $[T] native('ObservableDebounce')
operator throttle-time[T] { o $[T], ms Int } $[T] native('ObservableThrottleTime')
operator debounce-time[T] { o $[T], ms Int } $[T] native('ObservableDebounceTime')
operator take[T] { o $[T], n Int } $[T] native('ObservableTake')
operator take-last[T] { o $[T] } $[T] native('ObservableTakeLast')
operator take-while[T] { o $[T], f Lambda[T,Bool] } $[T] native('ObservableTakeWhile')
operator take-until[T] { o $[T], stop $[None] } $[T] native('ObservableTakeUntil')
operator count[T] { o $[T] } $[Int] native('ObservableCount')
operator collect[T] { o $[T], n Int default(-1) } $[List[T]] native('ObservableCollect')
operator buffer-time[T] { o $[T], ms Int } $[List[T]] native('ObservableBufferTime')
operator pairwise[T] { o $[T] } $[Pair[T,T]] native('ObservablePairwise')
operator buffer-count[T] { o $[T], n Int } $[Queue[T]] native('ObservableBufferCount')
operator map[A,B] { o $[A], f Lambda[A,B] } $[B] native('ObservableMap')
operator map-to[A,B] { o $[A], v B } $[B] native('ObservableMapTo')
operator filter[T] { o $[T], f Lambda[T,Bool] } $[T] native('ObservableFilter')
operator scan[A,B] { o $[A], v B, f Lambda[Pair[B,A],B] } $[B] native('ObservableScan')
operator combine-latest[A,B] { a $[A], b $[B] } $[Pair[A,B]] native('ObservableCombineLatest')
operator combine-latest[T] { l List[$[T]] } $[List[T]] native('ListObservableCombineLatest')
operator await[A,B] { o $[A], k Lambda[A,$[B]] } $[B] native('ObservableAwait')
operator then[T] { o $[None], k $[T] } $[T] native('ObservableThen')
operator with[T] { o $[T], bg $[None] } $[T] native('ObservableWith')
operator merge[T]  { l List[$[T]] } $[T] native('ListObservableMerge')
operator concat[T] { l List[$[T]] } $[T] native('ListObservableConcat')
operator merge[T]  { o1 $[T], o2 $[T] } $[T] native('ObservableMerge')
operator concat[T] { o1 $[T], o2 $[T] } $[T] native('ObservableConcat')
operator merge-map[A,B]   { o $[A], f Lambda[A,$[B]] } $[B] native('ObservableMergeMap')
operator concat-map[A,B]  { o $[A], f Lambda[A,$[B]] } $[B] native('ObservableConcatMap')
operator switch-map[A,B]  { o $[A], f Lambda[A,$[B]] } $[B] native('ObservableSwitchMap')
operator exhaust-map[A,B] { o $[A], f Lambda[A,$[B]] } $[B] native('ObservableExhaustMap')
const NumCPU Int native('NumCPU')
operator concurrent[T] { l List[$[T]], n Int default(NumCPU) } $[T] native('ListObservableConcurrent')
operator concurrent-map[A,B] { o $[A], f Lambda[A,$[B]], n Int default(NumCPU) } $[B] native('ObservableConcurrentMap')
operator fork-join[T] { l List[$[T]], n Int default(NumCPU) } $[List[T]] native('ListObservableForkJoin')
operator fork-join[A,B] { a $[A], b $[B], n Int default(NumCPU) } $[Pair[A,B]] native('ObservableForkJoin')

type Time native
method Time.String String native('TimeString')
operator -ms { t Time, u Time } Int native('TimeSubtractMillisecond')

type Lambda[A,B] native
operator *  [A,B,C] { f Lambda[B,C], g Lambda[A,B] } Lambda[A,C] {{ x => f(g(x)) }}
operator -> [A,B]   { x A, f Lambda[A,B] } B { f(x) }

type Maybe[OK] union {
    None,
    OK
}
type None native
const None None native('None')
method Maybe.List List[OK] {
    if (let value = this) {
        List(value)
    } else {
        List()
    }
}
operator ??[T] { value? Maybe[T], fallback T } T {
    if (let value = value?) {
        value
    } else {
        fallback
    }
}

type Result[OK] union {
    Error,
    OK
}
type Error native
function Error { msg String } Error native('Error')
// TODO: Error | is-EOF, is-cancel

type Pair[A,B] record {
    First   A,
    Second  B
}
type Triple[A,B,C] record {
    First   A,
    Second  B,
    Third   C
}
function Pair[A,B] { first A, second B } Pair[A,B] {
    new Pair(first, second)
}
function Triple[A,B,C] { first A, second B, third C } Triple[A,B,C] {
    new Triple(first, second, third)
}

type Lens1[Whole,Part] record {
    Value   Part,
    Update  Lambda[Part,Whole]
}
type Lens2[Abstract,Concrete] record {
    Value   Maybe[Concrete],
    Update  Lambda[Concrete,Abstract]
}
operator compose1[A,B,C] { ab Lens1[A,B], f Lambda[B,Lens1[B,C]] } Lens1[A,C] {
    new Lens1 {
        Value:  f(ab.Value).Value,
        Update: { c => ab.Update(f(ab.Value).Update(c)) }
    }
}
operator compose2[A,B,C] { ab Lens1[A,B], f Lambda[B,Lens2[B,C]] } Lens2[A,C] {
    new Lens2 {
        Value:  f(ab.Value).Value,
        Update: { c => ab.Update(f(ab.Value).Update(c)) }
    }
}
operator compose[A,B,C] { ab Lens2[A,B], f Lambda[Maybe[B],Lens2[B,C]] } Lens2[A,C] {
    new Lens2 {
        Value:  f(ab.Value).Value,
        Update: { c => ab.Update(f(ab.Value).Update(c)) }
    }
}

function Undefined[T] { msg String default('') } T native('Undefined')


